#include "RtmpPusherWindow.h"

/// <summary>
/// 推流命令 ffmpeg  -i test.flv  -f flv rtmp://192.168.1.44/live
/// 播放命令 ffplay rtmp://192.168.1.44/live  -fflags nobuffer
/// </summary>
/// <param name="parent"></param>
/// 


RtmpPusherWindow::RtmpPusherWindow(QWidget* parent)
	: QWidget(parent)
{
	this->resize(QSize(480, 480));
	this->setWindowTitle("rtmp推流");
	this->setWindowIcon(QIcon("images/opencv.png"));
	initViews();
}

void RtmpPusherWindow::initViews() {
	QVBoxLayout* vLayout = new QVBoxLayout(this);
	QHBoxLayout* hLayout = new QHBoxLayout(this);
	et = new EditText(this);
	et->setEnabled(true);
	et->setFixedHeight(30);
	et->setPlaceholderText("请输入数据源，如：文件，rtmp流、rtsp流");
	et->setText("E:/BaiduNetdiskDownload/video_test_files/test.flv");
	Button* btnChoiceBtn = new Button(this);
	btnChoiceBtn->setText("请选择推流文件");
	hLayout->addWidget(et);
	hLayout->addWidget(btnChoiceBtn);
	hLayout->setAlignment(Qt::AlignTop);
	this->setLayout(hLayout);

	etDst = new EditText(this);
	etDst->setEnabled(true);
	etDst->setFixedHeight(30);
	etDst->setPlaceholderText("请输入推流地址");
	etDst->setText("rtmp://124.223.218.248:1935/live/test");

	Button* btnStartPusher = new Button(this);
	btnStartPusher->setText("开始推流");

	vLayout->addLayout(hLayout);
	vLayout->addWidget(etDst);
	vLayout->addWidget(btnStartPusher);
	vLayout->setAlignment(Qt::AlignTop);

	//选择文件地址
	connect(btnChoiceBtn, &Button::clicked, this, [=]() {
		DialogUtils::showVideo(this, [=](QString filePath) {
			et->setText(filePath);
			});
		});
	//开始推流
	connect(btnStartPusher, &Button::clicked, [=]() {
		if (et->text().isEmpty())
		{
			QMessageBox::warning(this, "警告", "数据源不能为空");
		}
		else if (etDst->text().isEmpty())
		{
			QMessageBox::warning(this, "警告", "推流地址不能为空");
		}
		else//开始推流
		{
			startPusher();
		}
		});


}

void RtmpPusherWindow::startPusher() {
	///定义输入输出文件路径，此处的输入是一个视频文件路径，输出是rtmp流路径
	//char* inUrl = "E:/BaiduNetdiskDownload/video_test_files/test.flv";
	//char* outUrl = "rtmp://124.223.218.248:1935/live/test";
	std::string inUrl2 = et->text().toStdString();
	std::string outUrl2= etDst->text().toStdString();
	const char* inUrl = inUrl2.c_str();
	const char* outUrl = outUrl2.c_str();
	qDebug() << "推流数据源="<< inUrl;
	qDebug() << "推流地址=" << outUrl;
	//初始化网络库，让rtmp具有网络的能力
	avformat_network_init();

	///输入流
	//输入流解封装上下文
	AVFormatContext* ictx = NULL;
	//打开文件
	int ret = avformat_open_input(&ictx, inUrl, NULL, NULL);
	if (ret != 0) {
		printFFError(ret);
		return;
	}
	qDebug() << "open file " << inUrl << " success!";


	//获取音视频流信息
	ret = avformat_find_stream_info(ictx, NULL);
	if (ret < 0) {
		printFFError(ret);
		return;
	}
	qDebug() << "avformat_find_stream_info success";

	//打印堆栈信息
	//av_dump_format(ictx, 0, inUrl, 0);


	///输出流
	//创建输出流解封装上下文
	AVFormatContext* octx = NULL;
	ret = avformat_alloc_output_context2(&octx, 0, "flv", outUrl);
	if (ret < 0) {
		printFFError(ret);
		return;
	}
	qDebug() << "avformat_alloc_output_context2 success";

	//配置输出流
	for (int i = 0;i < ictx->nb_streams;i++) {
		//创建输出流
		const AVCodec* codec = avcodec_find_decoder(ictx->streams[i]->codecpar->codec_id);
		AVStream* outStream = avformat_new_stream(octx, codec);
		if (!outStream) {
			printFFError(0);
			return;
		}
		//复制配置信息
		ret = avcodec_parameters_copy(outStream->codecpar, ictx->streams[i]->codecpar);
		outStream->codecpar->codec_tag = 0;
	}
	qDebug() << "config output stream success";



	///rtmp推流
	///打开io
	ret = avio_open(&octx->pb, outUrl, AVIO_FLAG_WRITE);
	if (ret < 0) {
		printFFError(ret);
		return;
	}
	qDebug() << "avio_open success";

	//写入头信息
	ret = avformat_write_header(octx, NULL);
	if (ret < 0) {
		printFFError(ret);
		return;
	}
	qDebug() << "write_header success";

	AVPacket pkt;
	long long startTime = av_gettime();
	int testIndex = 0;
	for (;;) {
		ret = av_read_frame(ictx, &pkt);
		if (ret != 0 || pkt.size <= 0) {
			continue;
		}
		//qDebug() << "pkt.pts="<<pkt.pts<<flush;
		testIndex++;
		if (testIndex == 28) {
			qDebug() << "已经到达了出错的地方";
		}
		//计算转换pts、dts
		AVRational itime = ictx->streams[pkt.stream_index]->time_base;
		AVRational otime = octx->streams[pkt.stream_index]->time_base;
		pkt.pts = av_rescale_q_rnd(pkt.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
		pkt.dts = av_rescale_q_rnd(pkt.pts, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
		pkt.duration = av_rescale_q_rnd(pkt.duration, itime, otime, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_NEAR_INF));
		pkt.pos = -1;
		qDebug() << "pkt.pts2=" << pkt.pts << "testIndex=" << testIndex << flush;
		//推流
		//控制视频帧推送速度
		if (ictx->streams[pkt.stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			AVRational tb = ictx->streams[pkt.stream_index]->time_base;//时间基
			//已经过去的时间
			long long now = av_gettime() - startTime;
			long long dts = 0;
			dts = pkt.dts * (1000 * 1000 * r2d(tb));//获取到的是微妙
			if (dts > now)
				av_usleep(dts - now);
		}
		//推流
		ret = av_interleaved_write_frame(octx, &pkt);//内部会主动销毁pkt创建的内存
		if (ret < 0) {
			printFFError(ret);
			return;
		}
	}
}

void RtmpPusherWindow::printFFError(int code) {
	char buf[1024] = { 0 };
	av_strerror(code, buf, sizeof(buf));
	qDebug() << buf << endl;
}

double RtmpPusherWindow::r2d(AVRational r)
{
	return r.num == 0 || r.den == 0 ? 0. : (double)r.num / (double)r.den;
}

RtmpPusherWindow::~RtmpPusherWindow()
{

}
